package main

import (
	"fmt"
	"strconv"
	"strings"
)

// --------------返回值---------------------
// 声明一个函数，传入两个int类型整数，返回两个数相加的和， 方法名开头大写：表示该方法可以被本包和其他包引用
func Add(n1 int, n2 int) int { // 返回值只有一个，可以不加()
	return n1 + n2
}

// 声明一个函数，传入两个int类型整数，返回两个结果，一个是两数之和，一个是两数之差
func addAndSub(n1 int, n2 int) (add int, sub int) {
	add = n1 + n2
	sub = n1 - n2
	//return add, sub
	return
}

func avg1(n1 int, n2 int) int { // 返回值只标注：类型
	avgVal := n1 / n2
	return avgVal
}
func avg(n1 int, n2 int) (avg int) { // 返回值标注返回值：变量+类型，必须用括号扩住
	avg = n1 / n2
	return // 如果标注了返回值变量，可以不写返回的变量
}

//-----------值传递和引用传递-----------------
// 定义一个函数，将传递进来的参数的值变为10
func change1(num int) {
	num = 10
}

// 定义一个函数，将传递进来的参数的值变为10
func change2(num *int) {
	*num = 10
}

// -------------------------------函数作为形参------------------------
func testType(num int) {
	fmt.Println(num)
}
func testType2(num1 int, num2 int) {
	fmt.Println("testType2被执行，num1:", num1, "num2:", num2)
}
func testType3(num1 int, num2 int) {
	fmt.Println("testType3被执行，num1:", num1, "num2:", num2)
}

// 定义一个函数，把另外一个函数作为形参
func test02Type(num1 int, num2 int, testFunc func(int, int)) {
	fmt.Println("-----------test02Type 被执行")
	testFunc(num1, num2)
}

type MyFunc func(int)

func test03Type(num1 int, num2 int, myFunc MyFunc) {
	fmt.Println("-----------test02, myFunc")
}

// 函数类型
func funType() {
	// 函数也是一种数据类型，可以赋值给一个变量
	a := testType                                              //变量就是一-个函数类型的变量
	fmt.Printf("a的类型是: %T，testType函数的类型是: %T \n", a, testType) //a的类型是: func (int) , testType函数的类型是: f
	//通过该变量可以对函数调用
	a(10) //等价于 testType(10)

	// 函数作为形参: 可以灵活扩展操作
	test02Type(10, 2, testType2)
	test02Type(10, 3, testType3)

	a1 := testType2
	test02Type(10, 2, a1)

	test03Type(10, 20, a)
}

// ---------------函数作为返回值----------------
func testTypeReturnSum() func(num1 int, num2 int) (result int) {
	return func(num1 int, num2 int) (result int) {
		result = num1 + num2
		return
	}
}

func testTypeReturnAvg() func(num1 int, num2 int) (result int) {
	return func(num1 int, num2 int) (result int) {
		result = num1 / num2
		return
	}
}

// 根据不同类型处理不同操作
func testTypeReturnOpt() {
	var optFunc func(n1 int, n2 int) (result int)
	optFunc = testTypeReturnSum()
	r := optFunc(1, 2)
	fmt.Println("testTypeReturnSum", r)
	optFunc = testTypeReturnAvg()
	r = optFunc(4, 2)
	fmt.Println("testTypeReturnAvg", r)
}

// --------------------传统要求：返回值类型对应，顺序不能差--------------
func test04(num1 int, num2 int) (int, int) {
	result01 := num1 + num2
	result02 := num1 - num2
	return result01, result02
}

// 在返回参数上定义sum 和 sub 在return可以不写sum,sub
func test05(num1 int, num2 int) (sum int, sub int) {
	sum = num1 + num2
	sub = num1 - num2
	return
}

// ---------------------同包下函数的的定义 ---------------------
//同包下不同文件，定义相同的函数，编译不允许
// 同文件夹下的 go文件中定义的package如果和文件夹名不同，那么这个文件夹下的所有文件中的package声明必须都一致
/**
 * 给包起别名：
 * import(test "gocode/testproject/util5/demo09/calutils") 给这个文件包起别名，就只能用别名来调用
 * func main() {
 *  test.Add()
 * }
 */

// go -----------不支持方法重载
//func delay3(par int) {
//	fmt.Println("延迟调用...")
//}

// go -------------------可变参数------------------------------
func delay4(args ...int) {
	fmt.Println("可变参数的使用...")
	bim := []int{1, 2, 3, 4}
	delay5(bim...)        // 可以传一个切片
	delay5(1, 2, 3, 4, 5) // 也可以传多个值
}
func delay5(args ...int) {

}

/* 函数作为字段 */
type funcStruct struct {
	fn func() string // 结构体字段是一个函数类型，
}

func funcAsField() {
	d := struct {
		fn func() string
	}{
		fn: func() string { return "hello" },
	}
	d.fn()
}

/* 函数作为一个channel */
func channelOfFunc() {
	fc := make(chan func() string, 2)
	fc <- func() string {
		return "hello world"
	}
	println((<-fc)())
}

//***********************init函数, 可同时定义多个init****************************
func init() {
	fmt.Println("init函数在main调用之前被执行！")
}
func init() {
	fmt.Println("第二个init函数在main调用之前被执行，可以定义多个init函数")
}

// ------------------------变量和函数的调用顺序------------------
//*****************全部变量的定义，init函数的执行，main函数的调用 被调用的顺序？************1.全部变量2.init函数，3.main函数调用
// ****************a.go导入b.go文件，会先初始化b.go文件中的全局变量和init函数

// *****************************匿名函数*****************************

var result = func(num1 int, num2 int) int { // 外部匿名函数
	return num1 + num2
}(10, 20)

//func main() {
//
//	sub := func(num1 int, num2 int) int {  // 内部匿名函数
//		return num1 - num2
//	}
//	result := sub(30, 70) // 调用匿名函数
//	fmt.Println(result)
//}

// *******************闭包*****************************

func getSum() func(int) int {
	var sum int = 10 // 返回的匿名函数+匿名函数以外的变量sum 称:闭包
	return func(num int) int {
		sum += num
		return sum
	}
}

// *******************defer 关键字*****************************

func deferKeyword() { // 这个函数在遇到return，或者函数执行完成后从栈中取出执行
	// 延迟调用
	// 延迟调用会等待主函数都执行完成之后再执行。会按照delay3，delay2，delay1的顺序执行
	defer delay1() // 遇到defer关键字，会将后面的代码压入栈中，也会将相关的值压入栈中，不会随着函数后面的变化而变化
	defer delay2()
	defer delay3() // 例如：数据库连接关闭
	fmt.Println("主函数执行。。。。。。。")
}
func delay1() {
	fmt.Println("延迟调用...")
}
func delay2() {
	fmt.Println("延迟调用...")
}
func delay3() {
	fmt.Println("延迟调用...")
}

// *******************系统函数*****************************
func lenFunc() {

	var le = "golang你好" // golang中的汉子是utf-8编码，一个汉字3个字节
	fmt.Println(len(le))

	r := []rune(le)
	for i := 0; i < len(r); i++ {
		fmt.Printf("%c \n", r[i])
	}

}

// *******************int 转 string *****************************
func intToStr() {
	num1, _ := strconv.Atoi("666")
	fmt.Println(num1)
	itoa := strconv.Itoa(555)
	fmt.Println(itoa)
}

// *******************string 常用函数*****************************
func split() {
	count := strings.Count("javaandgoland", "go") // 统计有多少个go
	contains := strings.Contains("javaandgoland", "go")
	fmt.Println(count, contains)
	strings.EqualFold("go", "GO") // 不区分大小写比较
	index := strings.Index("javaandgoland", "go")
	fmt.Println(index) // 如果返回-1 表示没有
	strings.Replace("javaandgoland", "go", "golang", -1)
	strings.Split("go-python-java", "-")
	strings.ToLower("golang")
	strings.TrimSpace(" golang") // 去左右两遍的
	strings.Trim("~golang~", "~")
	strings.HasPrefix("http://www.baidu.com", "http") // 判断是否指定字符开头
	strings.HasSuffix("http://www.baidu.com", "com")

}

// *******************日期函数*****************************

func main1() {

	sub := func(num1 int, num2 int) int {
		return num1 - num2
	}
	result := sub(30, 70) // 调用匿名函数
	fmt.Println(result)

	// 只接受add的值，不接收差的值
	add, _ := addAndSub(1, 2)
	fmt.Println(add)

	// 定义局部变量
	num := 1
	fmt.Println("改变前num的值：", num)
	// 将实际参数传递给函数change
	change1(num)
	fmt.Println("改变后num的值：", num)

	// 将实际参数传递给函数change
	change2(&num)
	fmt.Println("改变后num的值：", num)

}
